﻿using pdftron.PDF;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows.UI.Xaml.Shapes;

using UIRect = Windows.Foundation.Rect;
using PDFRect = pdftron.PDF.Rect;
// The User Control item template is documented at http://go.microsoft.com/fwlink/?LinkId=234236

namespace CompleteReader.Dialogs
{
    public sealed partial class FindTextDialog : CloseableDialog
    {
        private pdftron.PDF.Tools.Controls.TextHighlighter _TextHighlighter;
        private PDFViewCtrl _PDFViewCtrl;
        public PDFViewCtrl PDFViewCtrl
        {
            set
            {
                _PDFViewCtrl = value;
            }
        }

        private SolidColorBrush _HighlightColor = new SolidColorBrush(Windows.UI.Color.FromArgb(100, 100, 0, 255));

        private IAsyncOperation<pdftron.PDF.PDFViewCtrlSelection> _SearchResult = null;
        private int _SelectionPageNumber;
        private List<Rectangle> _OnScreenSelection = new List<Rectangle>();
        private string _SearchString;
        private bool _CancelSearch = false;
        private bool _IsSearching = false;
        private pdftron.PDF.PDFViewCtrlSelection _CurrentSearchSelection;

        // search progress
        private DispatcherTimer _SearchProgressTimer;
        private int _TicksBeforeProgress = 0;

        public FindTextDialog()
        {
            this.InitializeComponent();
        }

        private void CloseButton_Click(object sender, RoutedEventArgs e)
        {
            CloseDialog();
        }

        public override void OpenDialog()
        {
            if (_Closed || _Closing)
            {
                SearchTextTerm.Focus(FocusState.Programmatic);
                this.Visibility = Windows.UI.Xaml.Visibility.Visible;
                _Closing = false;
                _Closed = false;
                _Opening = true;
                _Opened = false;
                SlideInSearchPanel.Begin();
                SlideInPrevNext.Begin();

                SearchTextTerm.Text = "";
                if (_PDFViewCtrl != null)
                {
                    _PDFViewCtrl.OnScale += PDFViewCtrl_OnScale;
                    _PDFViewCtrl.OnSize += PDFViewCtrl_OnSize;
                    _PDFViewCtrl.OnPageNumberChanged += PDFViewCtrl_OnPageNumberChanged;
                }
            }
        }

        private void SlideInCompleted(object sender, object e)
        {
            base.OpenDialog();
            SearchTextTerm.Focus(FocusState.Programmatic);
        }


        public override void CloseDialog()
        {
            ResetSearchProgress();
            if (_Opened || _Opening)
            {
                if (_TextHighlighter != null)
                {
                    _TextHighlighter.Detach();
                    _TextHighlighter = null;
                }
                ClearSelection();
                _Closing = true;
                _Closed = false;
                _Opening = false;
                _Opened = false;
                SlideOutSearchPanel.Begin();
                SlideOutPrevNext.Begin();
                if (_PDFViewCtrl != null)
                {
                    _PDFViewCtrl.OnScale -= PDFViewCtrl_OnScale;
                    _PDFViewCtrl.OnSize -= PDFViewCtrl_OnSize;
                }
            }
        }

        private void SlideOutCompleted(object sender, object e)
        {
            base.CloseDialog();
        }

        void PDFViewCtrl_OnScale()
        {
            ClearSelection();
        }

        void PDFViewCtrl_OnSize()
        {
            ClearSelection();
        }

        void PDFViewCtrl_OnPageNumberChanged(int current_page, int num_pages)
        {
            if (!_IsSearching)
            {
                if (!_PDFViewCtrl.GetVisiblePages().Contains(_SelectionPageNumber))
                {
                    ClearSelection();
                }
            }
        }

        private void SearchTextTerm_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (_TextHighlighter != null)
            {
                _TextHighlighter.Detach();
                _TextHighlighter = null;
            }
            TextNotFoundTextBlock.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
            ClearSelection();
            _PDFViewCtrl.CancelFindText();
            ResetSearchProgress();
            _CancelSearch = true;
            PreviousButton.IsEnabled = false;
            NextButton.IsEnabled = false;
            NextButton.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
            PreviousButton.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
        }

        private void SearchTextTerm_KeyDown(object sender, KeyRoutedEventArgs e)
        {
            if (e.Key == Windows.System.VirtualKey.Enter)
            {
                e.Handled = true;
                BeginSearch();
                NextButton.Focus(FocusState.Programmatic);
            }
            
        }

        private void SearchButton_Click(object sender, RoutedEventArgs e)
        {
            BeginSearch();
        }

        private void BeginSearch()
        {
            if (_IsSearching)
            {
                return;
            }
            _CancelSearch = false;
            _SearchString = SearchTextTerm.Text.Trim();
            if (_SearchString.Length > 0)
            {
                NextButton.IsEnabled = true;
                PreviousButton.IsEnabled = true;
                NextButton.Visibility = Windows.UI.Xaml.Visibility.Visible;
                PreviousButton.Visibility = Windows.UI.Xaml.Visibility.Visible;

                // Note that the search cannot be Canceled by using the _SearchResult.Cancel(),
                // Use mPDFView.CancelFindText() instead.
                _SearchResult = StartSearchAsync(_SearchString, false, false, false, false);

                if (_SearchResult != null)
                {
                    _SearchResult.Completed = SearchDelegate;
                }

                if (_TextHighlighter == null)
                {
                    _TextHighlighter = new pdftron.PDF.Tools.Controls.TextHighlighter(_PDFViewCtrl, _SearchString);
                }
            }
            else
            {
                NextButton.IsEnabled = false;
                PreviousButton.IsEnabled = false;
                NextButton.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
                PreviousButton.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
            }
        }

        private void PreviousButton_Click(object sender, RoutedEventArgs e)
        {
            if (_IsSearching)
            {
                return;
            }
            _SearchResult = StartSearchAsync(_SearchString, false, false, true, false);
            if (_SearchResult != null)
            {
                _SearchResult.Completed = SearchDelegate;
            }
        }

        private void NextButton_Click(object sender, RoutedEventArgs e)
        {
            if (_IsSearching)
            {
                return;
            }
            _SearchResult = StartSearchAsync(_SearchString, false, false, false, false);
            if (_SearchResult != null)
            {
                _SearchResult.Completed = SearchDelegate;
            }
        }

        IAsyncOperation<pdftron.PDF.PDFViewCtrlSelection> StartSearchAsync(String searchString, bool matchCase, bool matchWholeWord, bool searchUp, bool regExp)
        {
            _IsSearching = true;
            _TicksBeforeProgress = 20; // will be 1 second
            if (_SearchProgressTimer != null)
            {
                _SearchProgressTimer.Stop();
            }
            _SearchProgressTimer = new DispatcherTimer();
            _SearchProgressTimer.Tick += SearchProgressTimer_Tick;
            _SearchProgressTimer.Interval = TimeSpan.FromMilliseconds(50);
            _SearchProgressTimer.Start();
            TextNotFoundTextBlock.Visibility = Windows.UI.Xaml.Visibility.Collapsed;

            _SelectionPageNumber = -1;
            return _PDFViewCtrl.FindTextAsync(searchString, matchCase, matchWholeWord, searchUp, regExp);
        }

        void SearchProgressTimer_Tick(object sender, object e)
        {
            if (_TicksBeforeProgress > 0)
            {
                _TicksBeforeProgress--;
            }
            else
            {
                int progress = _PDFViewCtrl.GetFindTextProgress();
                SearchProgressRect.Width = SearchTextAndProgressGrid.ActualWidth * (progress / 100.0);
            }
        }

        void ResetSearchProgress()
        {
            SearchProgressRect.Width = 0;
            if (_SearchProgressTimer != null)
            {
                _SearchProgressTimer.Stop();
            }
        }


        public async void SearchDelegate(IAsyncOperation<pdftron.PDF.PDFViewCtrlSelection> asyncAction, AsyncStatus asyncStatus)
        {
            ResetSearchProgress();
            await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, delegate
            {
                _IsSearching = false;
                if (_Closing || _Closed)
                { 
                    return;
                }
                switch (asyncStatus)
                {
                    case AsyncStatus.Error:
                        //SearchResultOutput.Text = "Error";
                        break;
                    case AsyncStatus.Completed:
                        if (_CancelSearch)
                        {
                            return;
                        }
                        _CurrentSearchSelection = asyncAction.GetResults();
                        if (HighlightSelection(_CurrentSearchSelection))
                        {
                            // We don't want the PDFViewCtrl to have any selection, at this point.
                            // We got our quad(s), no one else should work with this selection after this.
                            _PDFViewCtrl.ClearSelection();

                            NextButton.IsEnabled = true;
                            PreviousButton.IsEnabled = true;
                            NextButton.Visibility = Windows.UI.Xaml.Visibility.Visible;
                            PreviousButton.Visibility = Windows.UI.Xaml.Visibility.Visible;
                            //SearchResultOutput.Text = "";
                        }
                        else
                        {
                            NextButton.IsEnabled = false;
                            PreviousButton.IsEnabled = false;
                            NextButton.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
                            PreviousButton.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
                            TextNotFoundTextBlock.Visibility = Windows.UI.Xaml.Visibility.Visible;
                            //SearchResultOutput.Text = "No match found";
                        }
                        break;
                }
            });
        }

        private void ClearSelection()
        {
            if (_OnScreenSelection == null)
            {
                return;
            }
            foreach (Rectangle rect in _OnScreenSelection)
            {
                Canvas parent = rect.Tag as Canvas;
                if (parent != null)
                {
                    parent.Children.Remove(rect);
                }
            }
            _OnScreenSelection.Clear();
        }

        /// <summary>
        /// Highlights the search result if any
        /// </summary>
        /// <param name="result">A text selection acquired by mPDFView.FindText</param>
        /// <returns>true if and only if the selections contains at least one highlight</returns>
        private bool HighlightSelection(pdftron.PDF.PDFViewCtrlSelection result)
        {
            if (result == null)
            {
                return false;
            }

            double[] quads = result.GetQuads();
            int numQuads = result.GetQuadArrayLength() / 8;
            _SelectionPageNumber = result.GetPageNum();
            int quadNumber = 0;
            List<UIRect> rects = new List<UIRect>();

            // get highlights in control (screen) space
            for (int i = 0; i < numQuads; i++)
            {
                quadNumber = i * 8;

                pdftron.Common.DoubleRef x1 = new pdftron.Common.DoubleRef(quads[quadNumber + 0]);
                pdftron.Common.DoubleRef y1 = new pdftron.Common.DoubleRef(quads[quadNumber + 1]);

                pdftron.Common.DoubleRef x2 = new pdftron.Common.DoubleRef(quads[quadNumber + 2]);
                pdftron.Common.DoubleRef y2 = new pdftron.Common.DoubleRef(quads[quadNumber + 3]);

                pdftron.Common.DoubleRef x3 = new pdftron.Common.DoubleRef(quads[quadNumber + 4]);
                pdftron.Common.DoubleRef y3 = new pdftron.Common.DoubleRef(quads[quadNumber + 5]);

                pdftron.Common.DoubleRef x4 = new pdftron.Common.DoubleRef(quads[quadNumber + 6]);
                pdftron.Common.DoubleRef y4 = new pdftron.Common.DoubleRef(quads[quadNumber + 7]);

                _PDFViewCtrl.ConvPagePtToScreenPt(x1, y1, _SelectionPageNumber);
                _PDFViewCtrl.ConvPagePtToScreenPt(x2, y2, _SelectionPageNumber);
                _PDFViewCtrl.ConvPagePtToScreenPt(x3, y3, _SelectionPageNumber);
                _PDFViewCtrl.ConvPagePtToScreenPt(x4, y4, _SelectionPageNumber);

                double left, right, top, bottom;

                left = Math.Min(x1.Value, Math.Min(x2.Value, Math.Min(x3.Value, x4.Value)));
                right = Math.Max(x1.Value, Math.Max(x2.Value, Math.Max(x3.Value, x4.Value)));
                top = Math.Min(y1.Value, Math.Min(y2.Value, Math.Min(y3.Value, y4.Value)));
                bottom = Math.Max(y1.Value, Math.Max(y2.Value, Math.Max(y3.Value, y4.Value)));

                rects.Add(new UIRect(left, top, right - left, bottom - top));
            }

            Canvas annotCanvas = _PDFViewCtrl.GetAnnotationCanvas();

            ClearSelection();

            // add highlight(s) to annotation canvas
            foreach (UIRect rect in rects)
            {
                Rectangle highlight = new Rectangle();
                highlight.Fill = _HighlightColor;
                highlight.Width = rect.Width;
                highlight.Height = rect.Height;
                Canvas.SetLeft(highlight, rect.Left + _PDFViewCtrl.GetAnnotationCanvasHorizontalOffset());
                Canvas.SetTop(highlight, rect.Top + _PDFViewCtrl.GetAnnotationCanvasVerticalOffset());
                annotCanvas.Children.Add(highlight);
                _OnScreenSelection.Add(highlight);
                highlight.Tag = annotCanvas;
            }

            return numQuads > 0;
        }

        void OnScaleHandler()
        {
            HighlightSelection(_CurrentSearchSelection);
        }
    }
}
